<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
 * PHP version 5
 *
 * This source file is subject to version 2.1 of the GNU Lesser General Public
 * License, that is bundled with this package in the file COPYING, available
 * through the world wide web at the following URI:
 * http://www.gnu.org/copyleft/lesser.html.
 *
 * @package    ZNF
 * @subpackage Config
 * @author     Alessandro Rossini <http://www.alessandrorossini.org>
 * @author     Graziano Liberati <http://www.liberati.org>
 * @author     Tomasz Kuter <evolic@interia.pl>
 * @copyright  2004-2007 The ZNF Development Team
 * @license    LGPL License 2.1 <http://www.gnu.org/copyleft/lesser.html>
 * @version    SVN $Id: AppConfig.php 44 2007-07-22 23:55:30Z evolic $
 * @since      Release 0.7.5
 * @link       http://www.zeronotice.org
 */

/**
 * <i>ZNF_Config_AppConfig</i> contains the collection of static application configuration informations.
 *
 * Returns the <i>ZNF_Config_AppConfig</i> object reference with a singleton
 * pattern.
 *
 * @access     public
 * @package    ZNF
 * @subpackage Config
 * @author     Alessandro Rossini <http://www.alessandrorossini.org>
 * @author     Graziano Liberati <http://www.liberati.org>
 * @author     Tomasz Kuter <evolic@interia.pl>
 * @copyright  2004-2007 The ZNF Development Team
 * @license    LGPL License 2.1 <http://www.gnu.org/copyleft/lesser.html>
 * @version    SVN $Id: AppConfig.php 44 2007-07-22 23:55:30Z evolic $
 * @since      Release 0.7.5
 * @link       http://www.zeronotice.org
 */
class ZNF_Config_AppConfig
{

    /**
     * Name of the configuration file.
     */
    const CONFIG_FILE = 'znf-app-config.xml';

    /**
     * The <i>ZNF_Config_AppConfig</i> object reference.
     *
     * @access protected
     * @staticvar ZNF_Config_AppConfig $_appConfig
     */
    static protected $_appConfig;

    /**
     * The array of the configuration file.
     *
     * @access protected
     * @var array $_config
     */
    protected $_config;

    /**
     * Private constructor to avoid <i>ZNF_Config_AppConfig</i> object creation.
     *
     * @access private
     */
    private function __construct()
    {
        $this->_parseConfiguration();
    }

    /**
     * Private clonator to avoid <i>ZNF_Config_AppConfig</i> object clonation.
     *
     * @access private
     */
    private function __clone()
    {
    }

    /**
     * Returns the reference to the <i>ZNF_Config_AppConfig</i> object with a singleton pattern.
     *
     * This object is shared by all the classes that require the application
     * configuration.
     *
     * @static
     * @access public
     * @return ZNF_Config_AppConfig
     */
    static public function getInstance()
    {
        if (!(isset(self::$_appConfig))) {
            self::$_appConfig = new ZNF_Config_AppConfig();
        }

        return self::$_appConfig;
    }

    /**
     * Parses and validates the configuration file.
     *
     * For performance reasons implements a caching mechanism that serializes the
     * configuration file in an internal structure.
     *
     * @access protected
     */
    protected function _parseConfiguration()
    {
        $xmlTimestamp = filemtime(ZNF::CONFIG_DIR . '/' . self::CONFIG_FILE);
        $serializedTimestamp = @filemtime(ZNF::CACHE_DIR . ZNF::CACHE_DIR . '/' . self::CONFIG_FILE . '.cache');

        if ($xmlTimestamp <= $serializedTimestamp) {
            $this->_config = unserialize(file_get_contents(ZNF::CACHE_DIR . '/' . self::CONFIG_FILE . '.cache'));
        } else {
            $configXml = new DOMDocument('1.0','utf-8');
            $configXml->load(ZNF::CONFIG_DIR . '/' . self::CONFIG_FILE);

            if (!$configXml) {
                throw new ZNF_Config_AppConfigException('Error loading the application configuration file, file is not well formed');
            }

            // quick hack to make ZNF work as a PEAR package, to be improved
            $dirName = dirname(__FILE__);
            $schemaPath = substr($dirName, 0, strpos($dirName, 'Config')) . "xsd/znf-app-config.xsd";
            if (!$configXml->schemaValidate($schemaPath)) {
                throw new ZNF_Config_AppConfigException('Error loading the application configuration file, file is not valid');
            }

            $xpath = new DOMXPath($configXml);
            $xpath->registerNamespace('app', 'http://www.zeronotice.org/ZNF/znf-app-config');
            $config = array();

            //get app settings
            $appDefaults = $xpath->query('/app:znf-app-config/app:app-settings');
            $config['app-settings']['theme']      = $appDefaults->item(0)->getAttribute('theme');

            if (!file_exists(ZNF::THEME_DIR . "/{$config['app-settings']['theme']}")) {
                throw new ZNF_Config_AppConfigException('Error loading the application configuration file, the selected theme is not present');
            }

            $config['app-settings']['lang']       = $appDefaults->item(0)->getAttribute('lang');

            if (!file_exists(ZNF::THEME_DIR . "/{$config['app-settings']['theme']}/{$config['app-settings']['lang']}")) {
                throw new ZNF_Config_AppConfigException('Error loading the application configuration file, the selected language is not present');
            }

            $config['app-settings']['locale']     = $appDefaults->item(0)->getAttribute('locale');
            $config['app-settings']['langAutodetect'] = $appDefaults->item(0)->getAttribute('langAutodetect');
            $config['app-settings']['sessionId']  = $appDefaults->item(0)->getAttribute('sessionId');

            // get default module
            $default = $xpath->query('/app:znf-app-config/app:modules/app:module[@default="true"]');

            if ($default->length == 1) {
                $config['modules']['default']['name']      = $default->item(0)->getAttribute('name');
                $config['modules']['default']['config']    = $default->item(0)->getAttribute('config');
                $config['modules']['default']['processor'] = $default->item(0)->getAttribute('processor');

            // check if only one default module is defined
            } elseif ($default->length > 1) {
                throw new ZNF_Config_ModulesConfigException('Error loading the module configuration file, only one module can be default');

            // check if at least one default module is defined
            } else {
                throw new ZNF_Config_ModulesConfigException('Error loading the module configuration file, at least one module must be default');
            }

            // get modules
            $modules = $xpath->query('/app:znf-app-config/app:modules/app:module');

            foreach ($modules as $module) {
                $moduleName = $module->getAttribute('name');
                $config['modules'][$moduleName]['name']      = $moduleName;
                $config['modules'][$moduleName]['config']    = $module->getAttribute('config');
                $config['modules'][$moduleName]['processor'] = $module->getAttribute('processor');
            }

            // get authentication configuration
            $authConfig = $xpath->query('/app:znf-app-config/app:auth-config');

            if ($authConfig->length > 0) {
                $config['authConfig']['path'] = $authConfig->item(0)->getAttribute('path');
            }

            // get wrong authorization configuration
            $wrongAuthConfig = $xpath->query('/app:znf-app-config/app:wrong-auth-config');

            if ($wrongAuthConfig->length > 0) {
                $config['wrongAuthConfig']['path'] = $wrongAuthConfig->item(0)->getAttribute('path');
            }

            file_put_contents(ZNF::CACHE_DIR . '/' . self::CONFIG_FILE . '.cache', serialize($config));
            $this->_config = $config;
        }
    }

    /**
     * Returns the application lang.
     *
     * @access public
     * @return string
     */
    public function getLang()
    {
        return $this->_config['app-settings']['lang'];
    }

    /**
     * Returns the application locale.
     *
     * @access public
     * @return string
     */
    public function getLocale()
    {
        return $this->_config['app-settings']['locale'];
    }

    /**
     * Returns the application autodetected language.
     *
     * @access public
     * @return string
     */
    public function getLangAutodetect()
    {
        return $this->_config['app-settings']['langAutodetect'];
    }

    /**
     * Returns the application session ID.
     *
     * @access public
     * @return string
     */
    public function getSessionId()
    {
        return $this->_config['app-settings']['sessionId'];
    }

    /**
     * Returns the application theme.
     *
     * @access public
     * @return string
     */
    public function getTheme()
    {
        return $this->_config['app-settings']['theme'];
    }

    /**
     * Returns the module for the defined <i>name</i>.
     *
     * @access public
     * @param string $module
     * @return array
     */
    public function findModule($module)
    {
        if ($module && array_key_exists($module, $this->_config['modules'])) {

            // extracts the configuration of the module requested
            return $this->_config['modules'][$module];
        } else {

            // extracts the configuration of the default module
            return $this->_config['modules']['default'];
        }
    }

    /**
     * Returns the authentication configuration.
     *
     * @access public
     * @return array
     */
    public function findAuthConfig()
    {
        if (isset($this->_config['authConfig'])) {

            // extracts the authentication configuration
            return $this->_config['authConfig'];
        }

        // authentication configuration not found
        return null;
    }

    /**
     * Returns the wrong authentication configuration.
     *
     * @access public
     * @return array
     */
    public function findWrongAuthConfig()
    {
        if (isset($this->_config['wrongAuthConfig'])) {

            // extracts the authentication configuration
            return $this->_config['wrongAuthConfig'];
        }

        // authentication configuration not found
        return null;
    }

    /**
     * Destroys the <i>ZNF_Config_AppConfig</i> object.
     *
     * @access public
     */
    public function __destruct()
    {
    }

}

/**
 * <i>ZNF_Config_AppConfigException</i> is the exception type for the <i>ZNF_Config_AppConfig</i> class.
 *
 * <i>ZNF_Config_AppConfigException</i> extends the <i>Exception</i> class of PHP5.
 *
 * @access     public
 * @package    ZNF
 * @subpackage Config
 * @author     Alessandro Rossini <http://www.alessandrorossini.org>
 * @author     Graziano Liberati <http://www.liberati.org>
 * @copyright  2004-2007 The ZNF Development Team
 * @license    LGPL License 2.1 <http://www.gnu.org/copyleft/lesser.html>
 * @version    SVN $Id: AppConfig.php 44 2007-07-22 23:55:30Z evolic $
 * @since      Release 0.7.5
 * @link       http://www.zeronotice.org
 */
class ZNF_Config_AppConfigException extends Exception
{
}

?>
